﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using AccessControl.Contracts.Models;
using AccessControl.DataManagers.Tables;
using AccessControl.DataManagers.Tables.Managers;
using AccessControl.Contracts.Data;

namespace AccessControl.DataManagers
{
	/// <summary>
	/// Represents the data manager 
	/// </summary>
	public class DataManager : ICamerasDataManager, IEntriesDataManager, IEventsDataManager, IMapsDataManager,
							   IOperatorsDataManager, ISensorsDataManager, IUsersDataManager
	{
		#region Fields

		bool _initialized = false;
		SqlConnection _connection = null;
		private static DataManager s_Instance;

		#endregion

		#region Private Constructor

		private DataManager()
		{
		}

		#endregion

		#region Public Properties

		public static DataManager Instance
		{
			get 
			{
				lock (typeof(DataManager))
				{
					if (s_Instance == null)
						s_Instance = new DataManager();

					return s_Instance;
				}
			}
		}

		#endregion

		#region Public Members

		/// <summary>
		/// Initializes this instance.
		/// </summary>
		/// <returns>Whether the initialization succeeded</returns>
		public bool Initialize()
		{
			if (!_initialized)
			{
				/* First connect to the database */
				if (!Connect())
					return false;

				/* Initialize the tables */
				InitializeTables();
			}

			/* And we are done */
			_initialized = true;
			return true;
		}

		/// <summary>
		/// Adds the camera.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(ICamera item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { CamerasManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the camera.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(ICamera item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { CamerasManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the camera.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(ICamera item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { CamerasManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<ICamera> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { CamerasManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Adds the entry.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(IEntry item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { EntriesManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the entry.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(IEntry item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { EntriesManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the entry.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(IEntry item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { EntriesManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<IEntry> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EntriesManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Adds the map.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(IMap item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { MapsManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the map.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(IMap item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { MapsManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the map.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(IMap item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();


				try { MapsManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<IMap> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { MapsManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Adds the operator.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(IOperator item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { OperatorsManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the operator.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(IOperator item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { OperatorsManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the operator.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(IOperator item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { OperatorsManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<IOperator> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { OperatorsManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}
		
		/// <summary>
		/// Adds the sensor.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(ISensor item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();
			
				try { SensorsManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the sensor.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(ISensor item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { SensorsManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the sensor.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(ISensor item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { SensorsManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<ISensor> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { SensorsManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Adds the user.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Add(IUser item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();
			
				try { UsersManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the user.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Update(IUser item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { UsersManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the user.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns>Whether the operation succeeded</returns>
		public bool Delete(IUser item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { UsersManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<IUser> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { UsersManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Adds the event.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns></returns>
		public bool Add(IEvent item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.Add(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Updates the event.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns></returns>
		public bool Update(IEvent item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.Update(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Deletes the event.
		/// </summary>
		/// <param name="item">The item.</param>
		/// <returns></returns>
		public bool Delete(IEvent item)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.Delete(item); }
				catch { return false; }

				return true;
			}
		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public bool FillList(IList<IEvent> items)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.FillList(items); }
				catch { return false; }

				return true;
			}

		}

		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <param name="startTime">The start time.</param>
		/// <param name="endTime">The end time.</param>
		/// <returns></returns>
		public bool FillList(IList<IEvent> items, DateTime startTime, DateTime endTime)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.FillList(items, startTime, endTime); }
				catch { return false; }

				return true;
			}
		}
		/// <summary>
		/// Fills the list.
		/// </summary>
		/// <param name="items">The items.</param>
		/// <param name="startTime">The start time.</param>
		/// <param name="endTime">The end time.</param>
		/// <param name="sensors">The sensors.</param>
		/// <returns></returns>
		public bool FillList(IList<IEvent> items, DateTime startTime, DateTime endTime, IList<int> sensors)
		{
			lock (this)
			{
				if (!_initialized)
					throw new DatabaseNotInitializedException();

				try { EventsManager.Instance.FillList(items, startTime, endTime, sensors); }
				catch { return false; }

				return true;
			}
		}

		#endregion

		#region Private Members

		/// <summary>
		/// Connects this instance.
		/// </summary>
		/// <returns></returns>
		private bool Connect()
		{
			bool bResult = true;

			/* First we open the connection */
			_connection = new SqlConnection(Connection.ConnectionString);

			try
			{
				_connection.Open();
			}
			catch
			{
				bResult = TryCreateDatabase();
			}

			return bResult;
		}

		/// <summary>
		/// Tries the create database.
		/// </summary>
		/// <returns></returns>
		private bool TryCreateDatabase()
		{
			SqlCommand command;

			/* Open a connection with no db specification */
			_connection = new SqlConnection("Data Source=(local)\\SQLExpress;Integrated Security=true");
			try { _connection.Open(); }
			catch { return false; }

			/* Try to create the data base */
			command = _connection.CreateCommand();
			/* We want to create the database */
			command.CommandText = "CREATE DATABASE AccessControl";
			try
			{
				command.ExecuteNonQuery();
			}
			catch
			{
				_connection.Close();
				return false;
			}

			try
			{
				_connection.ChangeDatabase("AccessControl");
			}
			catch
			{
				_connection.Close();
				return false;
			}

			/* Create the tables */
			try
			{
				CreateTables();
			}
			catch
			{
				_connection.Close();
				return false;
			}

			return true;
		}

		/// <summary>
		/// Creates the tables.
		/// </summary>
		private void CreateTables()
		{
			CamerasManager.Instance.Create(_connection);
			EntriesManager.Instance.Create(_connection);
			MapsManager.Instance.Create(_connection);
			OperatorsManager.Instance.Create(_connection);
			SensorsManager.Instance.Create(_connection);
			UsersManager.Instance.Create(_connection);
			EventsManager.Instance.Create(_connection);
		}

		/// <summary>
		/// Initializes the tables.
		/// </summary>
		private void InitializeTables()
		{
			CamerasManager.Instance.Initialize(_connection);
			EntriesManager.Instance.Initialize(_connection);
			MapsManager.Instance.Initialize(_connection);
			OperatorsManager.Instance.Initialize(_connection);
			SensorsManager.Instance.Initialize(_connection);
			UsersManager.Instance.Initialize(_connection);
			EventsManager.Instance.Initialize(_connection);
		}

		#endregion
	}
}
